/*
 * $Id: SyncHttpConnection.java 406 2008-04-19 02:13:03Z jasta $
 *
 * Copyright (C) 2008 Josh Guilfoyle <jasta@devtcg.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

package org.devtcg.syncml.transport;

import java.net.URI;
import java.io.*;
import org.xmlpull.v1.*;
import org.kxml2.wap.syncml.SyncML;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;

import org.devtcg.syncml.protocol.SyncPackage;

public class SyncHttpConnection extends SyncConnection
{
	protected HttpClient mClient;
	protected PostMethod mLastMethod;
	protected String mURI;

	private InputStream mResponse;

	public SyncHttpConnection(String uri)
	{
		this(uri, uri);
	}

	public SyncHttpConnection(String uri, String name)
	{
		super(name);
		mURI = uri;
	}

	public void open()
	{
		mOpened = true;
		mClient = new HttpClient(new SimpleHttpConnectionManager());
	}

	public void close()
	{
		/* XXX */
		mOpened = false;

		if (mLastMethod != null)
			mLastMethod.releaseConnection();

//		mClient.getHttpConnectionManager().shutdown();
		mClient = null;
	}

	public void sendPackage(SyncPackage msg)
	  throws IOException
	{
		/* Lame :) */
		if (mLastMethod != null)
			mLastMethod.releaseConnection();

		SMLWbxmlByteArrayOutputStream out = new SMLWbxmlByteArrayOutputStream();

		XmlSerializer xs = SyncML.createSerializer();
		xs.setOutput(out, null);
		msg.write(xs);

		PostMethod post = new PostMethod(mURI);
		post.setRequestEntity
		 (new ByteArrayRequestEntity(out.toByteArrayAvoidCopy(),
		  "application/vnd.syncml+wbxml"));

		try
		{
			int status = mClient.executeMethod(post);
			mLastMethod = post;
//			mResponse = post.getResponseBodyAsStream();

//			System.out.println("Writing to test.out...");
//			FileOutputStream fout = new FileOutputStream("test.out");
//
//			byte[] b = new byte[4096];
//			int n;
//
//			while ((n = mResponse.read(b)) != -1)
//				fout.write(b, 0, n);
//
//			fout.close();
		}
		catch (IOException e)
		{
			post.releaseConnection();
			throw e;
		}
	}

	public InputStream recvPackage()
	  throws IOException
	{
		if (mLastMethod == null)
			throw new IllegalStateException("You must call sendPackage before you can receive a response");

		InputStream in;

		try {
			in = mLastMethod.getResponseBodyAsStream();
		} catch (IOException e) {
			mLastMethod.releaseConnection();
			mLastMethod = null;
			throw e;
		}

		return in;
	}

	public void releaseConnection()
	{
		if (mLastMethod != null)
		{
			mLastMethod.releaseConnection();
			mLastMethod = null;
		}
	}

	/**
	 * Accessor which permits callers to reuse the HTTP connection pool
	 * managed by this SyncML session.  This is useful for downloading
	 * out-of-band (non-SyncML) data from the server as part of the
	 * synchronization process.
	 */
	public HttpClient getHttpClient()
	{
		if (mOpened == false)
			throw new IllegalStateException("Open this SyncConnection first");

		if (mLastMethod != null)
			throw new IllegalStateException("Invoke releaseConnection first");

		return mClient;
	}

	/* XXX: Hack to fixup kxml2.  This should be patched upstream. */
	private static class SMLWbxmlByteArrayOutputStream extends ByteArrayOutputStream
	{
		public SMLWbxmlByteArrayOutputStream()
		{
			super();
		}

		@Override
		public void write(int oneByte)
		{
			/* Instead of writing an unknown public ID, write the SYNCML11 id
			 * so that libsyncml can read it. */
			if (count == 1 && oneByte == 0x01)
			{
				super.write(0x9f);
				super.write(0x53);
			}
			else
			{
				super.write(oneByte);
			}
		}

		public byte[] toByteArrayAvoidCopy()
		{
			return buf;
		}
	}
}
